iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0

上一篇我們用 Python 內建的 sqlite3 實際操作了資料庫,也看到資料真的被保存下來。

不過那時候的程式長這樣:

cursor.execute("INSERT INTO tasks (name, completed) VALUES (?, ?)", ("洗碗", 0))
rows = cursor.execute("SELECT id, name, completed FROM tasks").fetchall()

如果要把這些放進我們的 Flask API,會發現有幾件事有點麻煩。

自己寫 SQL 會遇到什麼問題?

第一,查出來的資料不是我們要的格式

SELECT 回傳的是 tuple:

(1, '洗碗', 0)

但我們的 API 要回傳的是:

{"id": 1, "name": "洗碗", "completed": false}

所以每次查完都要自己轉一次,欄位一多就很容易出錯,順序寫錯也不會有人提醒。

第二,SQL 字串會散落在程式各處

五個 API 就有五段 SQL,之後如果欄位改名,要一段一段找出來改。

第三,換資料庫就要重寫

SQLite、MySQL、PostgreSQL 的 SQL 其實有一些差異,換資料庫的時候可能要改不少地方。

這些問題,就是 ORM 想要幫我們解決的。

ORM 是什麼?

ORM = Object Relational Mapping(物件關聯對映)。

名字看起來很難,但概念其實很單純:

把資料庫的「表格」對應成程式裡的「類別」,讓我們可以用寫 Python 的方式操作資料庫。

對應關係是這樣:

資料表(Table) → 類別(class)
一筆資料(Row) → 一個物件
欄位(Column) → 物件的屬性

拿 Day 18 的 tasks 表來對照:

資料庫裡是:

id | name | completed
1 | 洗碗 | 0

在程式裡就會變成:

task = Task(name="洗碗", completed=False)

task.name # 洗碗
task.completed # False

新增資料原本要寫:

INSERT INTO tasks (name, completed) VALUES ('洗碗', 0);

用 ORM 就變成:

task = Task(name="洗碗", completed=False)
db.session.add(task)
db.session.commit()

查詢原本要寫:

SELECT * FROM tasks;

用 ORM 大概會變成:

db.session.scalars(db.select(Task)).all()

查出來的結果直接就是一個一個 Task 物件,可以用 task.name 取值,不用再自己把 tuple 轉成字典。

最後 SQL 還是會被執行,只是改由 ORM 幫我們產生出來。

ORM 的好處和代價

好處:

用 Python 的方式寫,不用一直切換去想 SQL
查出來就是物件,跟程式的其他部分比較好接
換資料庫時,多數情況只要改設定
會自動幫我們做參數化查詢,比較不容易寫出有 SQL Injection 風險的程式(Day 27、28 會講)

代價(也要誠實講):

多了一層東西要學,出問題時要知道它背後產生了什麼 SQL
很複雜的查詢,用 ORM 寫可能比直接寫 SQL 還難懂
不小心寫出效率很差的查詢時,自己不容易發現

不過對現在的我們來說,ORM 的幫助比較大,所以接下來會使用它。

Flask-SQLAlchemy 是什麼?

先講一下名字容易混淆的地方:

SQLAlchemy
Python 界最常用的 ORM 套件,本身跟 Flask 沒有關係,寫純 Python 也可以用。

Flask-SQLAlchemy
把 SQLAlchemy 包成 Flask 的擴充套件,幫我們處理跟 Flask 有關的設定,例如連線的建立與關閉。

這跟前面 Flask 和 Flask-RESTX 的關係有點像:
真正做事的是 SQLAlchemy,Flask-SQLAlchemy 是讓它在 Flask 裡用起來更順。

安裝

一樣在有啟動虛擬環境的終端機輸入:

pip install Flask-SQLAlchemy==3.1.1

裝好之後可以確認一下:

pip show Flask-SQLAlchemy

終端機安裝 Flask-SQLAlchemy 成功的畫面
https://ithelp.ithome.com.tw/upload/images/20261003/201838658zpXoGaK8w.png

設定資料庫連線

接著在 app.py 裡加入設定。

今天的目標是「能連上」,所以先加最基本的部分:

from flask import Flask
from flask_restx import Api, Resource, fields
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 設定要連到哪個資料庫
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'

db = SQLAlchemy(app)

api = Api(app, title='Task API', version='1.0')

一行一行看:

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'

這是在告訴 SQLAlchemy「要連到哪個資料庫」。

這串東西叫做連線字串,格式大致是:

資料庫種類://帳號:密碼@位置/資料庫名稱

因為 SQLite 只是一個檔案,不需要帳號密碼,所以寫起來特別短:

sqlite:///tasks.db

如果之後換成 MySQL 或 PostgreSQL,大概會長這樣(先看看就好):

mysql+pymysql://user:password@localhost/tasks
postgresql+psycopg://user:password@localhost/tasks

換資料庫的時候,主要改的就是這一行。

(正式環境的帳號密碼不會像這樣直接寫在程式裡,通常會放在環境變數,Day 26 會再提到。)

db = SQLAlchemy(app)

建立 SQLAlchemy 物件,並把 Flask app 傳給它。

跟 Day 11 的 api = Api(app) 很像,都是「把套件跟 Flask app 接起來」。

之後所有跟資料庫有關的操作,都會透過這個 db。

一個很容易卡住的地方:instance 資料夾

我們設定的是:

sqlite:///tasks.db

看起來好像會在專案資料夾直接建立 tasks.db。

但 Flask-SQLAlchemy 3.x 之後,SQLite 的相對路徑是以 Flask 的 instance 資料夾為基準。

所以實際上檔案會出現在:

你的專案/instance/tasks.db

第一次看到的時候會有點困惑,以為資料庫沒建立成功,其實只是放在 instance 裡面。

instance 資料夾是 Flask 用來放「跟這台機器有關、不該進版控」的東西,資料庫檔案放在這裡其實蠻合理的。

(之後如果用 Git,通常會把 instance/ 加進 .gitignore。)

今天做了什麼?

認識 ORM:把資料表對應成類別,用 Python 的方式操作資料庫
認識 Flask-SQLAlchemy:把 SQLAlchemy 接進 Flask 的擴充套件
安裝套件、設定 SQLALCHEMY_DATABASE_URI、建立 db 物件

不過現在只是「設定好了要連哪個資料庫」,程式裡還沒有任何一張表。

資料庫還不知道我們要存的作業有哪些欄位。

下一篇就來建立第一個 Model,讓 Flask-SQLAlchemy 幫我們把資料表建出來。


上一篇
Day 18|SQL 資料庫入門
下一篇
Day 20|Flask-SQLAlchemy 入門
系列文
從零開始的後端開發:用 Flask 實作 REST API,搞懂 API 與資料庫之間如何協作 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言